﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Qt
{
    /// <summary>
    /// QtMapper
    /// </summary>
    public class QtMapper : IMapper
    {
        private Dictionary<int, List<MapperModel>> m_MapperRecord = new Dictionary<int, List<MapperModel>>();

        /// <summary>
        /// 绑定关系
        /// </summary>
        /// <typeparam name="TDst"></typeparam>
        /// <typeparam name="TSrc"></typeparam>
        /// <param name="func">转换函数</param>
        public void Bind<TDst, TSrc>(Func<TSrc, TDst> func)
        {
            if (func == null)
            {
                throw new ArgumentNullException();
            }

            int srcTypeHashcode = typeof(TSrc).GetHashCode();
            lock(m_MapperRecord)
            {
                if (!m_MapperRecord.ContainsKey(srcTypeHashcode))
                {
                    m_MapperRecord.Add(srcTypeHashcode, new List<MapperModel>());
                }

                if (m_MapperRecord[srcTypeHashcode].Count(x => x.DstType == typeof(TDst)) >= 1)
                {
                    throw new Exception("已经存在该对应关系");
                }

                MapperModel<TDst, TSrc> mapperModel = new MapperModel<TDst, TSrc>(func);
                m_MapperRecord[srcTypeHashcode].Add(mapperModel);
            }
        }

        /// <summary>
        /// 根据源类型转换到目标类型(适用1对1绑定)
        /// </summary>
        /// <typeparam name="TSrc"></typeparam>
        /// <param name="src"></param>
        /// <returns></returns>
        public dynamic Get<TSrc>(TSrc src)
        {
            if (typeof(TSrc).IsArray)
            {
                //数组
                Type innerSrcType = typeof(TSrc).GetElementType();

                MethodInfo method = (MethodBase.GetCurrentMethod() as MethodInfo).MakeGenericMethod(new Type[] { innerSrcType });

                Array srcArray = src as Array;
                dynamic[] dstArray = new dynamic[srcArray.Length];
                for (int i = 0, count = srcArray.Length; i < count; ++i)
                {
                    dynamic ret = method.Invoke(this, new object[] { srcArray.GetValue(i) });
                    dstArray[i] = ret;
                }
                return dstArray;
            }
            else if (IsEnumerable(typeof(TSrc)))
            {
                //其他可迭代
                Type innerSrcType = typeof(TSrc).GetGenericArguments()[0];

                MethodInfo method = (MethodBase.GetCurrentMethod() as MethodInfo).MakeGenericMethod(new Type[] { innerSrcType });
                IList<dynamic> dst = new List<dynamic>();

                foreach (var x in (src as IEnumerable))
                {
                    object ret = method.Invoke(this, new object[] { x });
                    dst.Add(ret);
                }

                return dst;
            }
            else
            {
                if (src == null)
                {
                    throw new ArgumentNullException();
                }

                int srcTypeHashcode = typeof(TSrc).GetHashCode();

                if (!m_MapperRecord.ContainsKey(srcTypeHashcode) || m_MapperRecord[srcTypeHashcode].Count == 0)
                {
                    throw new Exception($"不存在{typeof(TSrc).FullName}的任何对应关系");
                }

                if (m_MapperRecord[srcTypeHashcode].Count != 1)
                {
                    throw new Exception($"存在{typeof(TSrc).FullName}的多个对应关系");
                }

                MapperModel mapperModel = m_MapperRecord[srcTypeHashcode].First();

                Type conveterType = typeof(MapperModel<,>).MakeGenericType(new Type[] { mapperModel.DstType, typeof(TSrc) });

                return conveterType.GetMethod("Convert").Invoke(mapperModel, new object[] { src });
            }
        }

        /// <summary>
        /// 根据源类型转换到目标类型(适用1对n绑定)
        /// </summary>
        /// <typeparam name="TDst"></typeparam>
        /// <typeparam name="TSrc"></typeparam>
        /// <param name="src"></param>
        /// <returns></returns>
        public TDst Get<TDst, TSrc>(TSrc src)
        {
            if (typeof(TSrc).IsArray)
            {
                //数组
                if (!typeof(TDst).IsArray)
                {
                    throw new Exception($"不存在{typeof(TSrc).FullName}到{typeof(TDst).FullName}的任何对应关系");
                }

                Type innerDstType = typeof(TDst).GetElementType();
                Type innerSrcType = typeof(TSrc).GetElementType();

                MethodInfo method = (MethodBase.GetCurrentMethod() as MethodInfo).MakeGenericMethod(new Type[] { innerDstType, innerSrcType });
                
                Array srcArray = src as Array;
                Array dstArray = Array.CreateInstance(innerDstType, srcArray.Length);
                for (int i = 0,count= dstArray.Length; i < count; ++i)
                {
                    object ret = method.Invoke(this, new object[] { srcArray.GetValue(i) });
                    dstArray.SetValue(ret, i);
                }
                return (TDst)(object)dstArray;
            }
            else if (IsEnumerable(typeof(TSrc)))
            {
                //其他可迭代
                if (!IsEnumerable(typeof(TDst)))
                {
                    throw new Exception($"不存在{typeof(TSrc).FullName}到{typeof(TDst).FullName}的任何对应关系");
                }

                Type innerDstType = typeof(TDst).GetGenericArguments()[0];
                Type innerSrcType = typeof(TSrc).GetGenericArguments()[0];

                MethodInfo method = (MethodBase.GetCurrentMethod() as MethodInfo).MakeGenericMethod(new Type[] { innerDstType, innerSrcType });
                Type dstType = typeof(List<>).MakeGenericType(innerDstType);
                IList dst = Activator.CreateInstance(dstType) as IList;

                foreach(var x in (src as IEnumerable))
                {
                    object ret= method.Invoke(this, new object[] { x });
                    dst.Add(ret);
                }

                return (TDst)dst;
            }
            else
            {
                if (src == null)
                {
                    throw new ArgumentNullException();
                }

                int srcTypeHashcode = typeof(TSrc).GetHashCode();

                if (!m_MapperRecord.ContainsKey(srcTypeHashcode) || m_MapperRecord[srcTypeHashcode].Count == 0)
                {
                    throw new Exception($"不存在{typeof(TSrc).FullName}的任何对应关系");
                }

                List<MapperModel> mapperModels = m_MapperRecord[srcTypeHashcode];
                MapperModel mapperModel = mapperModels.SingleOrDefault(x => x.DstType == typeof(TDst));
                if (mapperModel == default(MapperModel))
                {
                    throw new Exception($"不存在{typeof(TSrc).FullName}到{typeof(TDst).FullName}的任何对应关系");
                }

                MapperModel<TDst, TSrc> mapperModelConverter = mapperModel as MapperModel<TDst, TSrc>;
                return mapperModelConverter.Convert(src);
            }
        }

        private bool IsEnumerable(Type t)
        {
            if (t == null)
            {
                return false;
            }
            return t.GetInterface("IEnumerable") != null;
        }
    }

    abstract class MapperModel
    {
        public Type DstType { protected set; get; }
    }

    class MapperModel<TDst, TSrc> : MapperModel
    {
        private Func<TSrc,TDst> MapperFunc = null;

        public MapperModel(Func<TSrc, TDst> mapperFunc)
        {
            DstType = typeof(TDst);
            MapperFunc = mapperFunc;
        }

        public TDst Convert(TSrc src)
        {
            if (src.GetType() != typeof(TSrc))
            {
                throw new Exception("转换源类型不匹配");
            }
            return MapperFunc.Invoke(src);
        }
    }
}
